package jmine.tec.database.query;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jmine.tec.database.query.columns.BooleanColumnFactory;
import jmine.tec.database.query.columns.ClobColumnFactory;
import jmine.tec.database.query.columns.DateColumnFactory;
import jmine.tec.database.query.columns.NumericColumnFactory;
import jmine.tec.database.query.columns.StringColumnFactory;
import jmine.tec.database.query.columns.TimeColumnFactory;
import jmine.tec.database.query.columns.TimestampColumnFactory;
import jmine.tec.database.query.lazy.Lazy;

import org.hibernate.jdbc.Work;

import bancosys.tec.persist.controller.PersistenceEnabledController;
import bancosys.tec.persist.hibernate.PartitionSessionFactory;

/**
 * Base de dados que se pode consultar.
 * 
 * @author lundberg
 */
public class Database {

    private static final String HIBERNATE_DEFAULT_SCHEMA = "hibernate.default_schema";

    private final PersistenceEnabledController controller;

    private final String schema;

    private final Lazy<Map<String, Table>> tables = new Lazy<Map<String, Table>>(new TableLoader(this));

    private Map<Integer, ColumnFactory> columnFactories = new HashMap<Integer, ColumnFactory>();

    /**
     * Construtor
     * 
     * @param controller controller
     */
    public Database(PersistenceEnabledController controller) {
        this.controller = controller;
        this.schema =
                ((PartitionSessionFactory) Database.this.controller.getSessionFactory()).getConfiguration().getProperty(
                        HIBERNATE_DEFAULT_SCHEMA);
        this.addColumnFactory(new NumericColumnFactory());
        this.addColumnFactory(new StringColumnFactory());
        this.addColumnFactory(new DateColumnFactory());
        this.addColumnFactory(new TimeColumnFactory());
        this.addColumnFactory(new TimestampColumnFactory());
        this.addColumnFactory(new BooleanColumnFactory());
        this.addColumnFactory(new ClobColumnFactory());
    }

    /**
     * Adiciona uma fábrica de colunas
     * 
     * @param columnFactory columnFactory
     */
    public void addColumnFactory(ColumnFactory columnFactory) {
        for (Integer supportedType : columnFactory.getSupportedTypes()) {
            this.columnFactories.put(supportedType, columnFactory);
        }
    }

    /**
     * Busca todas as tabelas que podem ser consultadas.
     * 
     * @return List of Tables
     */
    public List<Table> findAllTables() {
        return new ArrayList<Table>(this.tables.get().values());
    }

    /**
     * Busca a tabela pelo nome.
     * 
     * @param name name
     * @return Table
     */
    public Table findTable(String name) {
        return this.tables.get().get(name);
    }

    /**
     * Executa uma tarefa que utiliza a conexão com o banco.
     * 
     * @param work work
     */
    void doWork(Work work) {
        this.controller.getSessionFactory().getCurrentSession().doWork(work);
    }

    /**
     * Busca o nome do schema que está sendo consultado.
     * 
     * @return schema
     */
    public String getSchema() {
        return this.schema;
    }

    /**
     * @return the columnFactories
     */
    public Map<Integer, ColumnFactory> getColumnFactories() {
        return Collections.unmodifiableMap(this.columnFactories);
    }
}
